module cyclic_code_decode(
    input  wire        clk          ,
    input  wire        rst_n        ,
    input  wire        start        ,
    input  wire [22:0] data_in      ,
    output reg         busy         ,
    output reg         done         ,
    output wire [11:0] data_out     ,
    output wire        error_trapped
);
    reg [ 5:0] cnt;
    reg [22:0] data_buf;
    reg [10:0] lfsr;
    reg        need_to_shift;
    reg        error_trapped_dff1;

    wire [10:0] s0 = lfsr;
    wire [10:0] s0_minus_s1 = lfsr ^ 11'b011_0110_0110;
    wire [10:0] s0_minus_s2 = lfsr ^ 11'b110_1100_1100;
    wire [ 3:0] weight_of_s0          = count_one(s0);
    wire [ 3:0] weight_of_s0_minus_s1 = count_one(s0_minus_s1);
    wire [ 3:0] weight_of_s0_minus_s2 = count_one(s0_minus_s2);
    wire        error_trapped_posedge;

    assign data_out = data_buf[22:11];

    assign error_trapped = cnt >= 'd24 &&
                           (weight_of_s0 <= 'd3 || 
                           weight_of_s0_minus_s1 <= 'd2 || 
                           weight_of_s0_minus_s2 <= 'd2);
    assign error_trapped_posedge = !error_trapped_dff1 && error_trapped;


    always @(posedge clk, negedge rst_n) begin
        busy <= (!rst_n || done) ? 1'b0 : 
                start ? 1'b1 :
                busy;

        done <= !rst_n ? 1'b0 :
                (cnt == 'd24 && error_trapped) ? 1'b1 :
                (cnt == 'd47) ? 1'b1 :
                1'b0;
        
        cnt <= (!rst_n || done) ? 'd0 :
               (start || busy) ? cnt + 'd1 : 
               cnt;
        
        error_trapped_dff1 <= error_trapped;
    end

    always @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            lfsr <= 11'b0;
        end else if (start) begin
            lfsr <= 11'b0;
        end else if (cnt >= 'd1 && cnt <= 'd23) begin
            lfsr[ 0] <= lfsr[10] ^ data_buf[22];
            lfsr[ 1] <= lfsr[ 0]           ;
            lfsr[ 2] <= lfsr[ 1] ^ lfsr[10];
            lfsr[ 3] <= lfsr[ 2]           ;
            lfsr[ 4] <= lfsr[ 3] ^ lfsr[10];
            lfsr[ 5] <= lfsr[ 4] ^ lfsr[10];
            lfsr[ 6] <= lfsr[ 5] ^ lfsr[10];
            lfsr[ 7] <= lfsr[ 6]           ;
            lfsr[ 8] <= lfsr[ 7]           ;
            lfsr[ 9] <= lfsr[ 8]           ;
            lfsr[10] <= lfsr[ 9] ^ lfsr[10];
        end else if (cnt >= 'd24 && cnt <= 'd46 && !error_trapped) begin
            lfsr[ 0] <= lfsr[10]           ;
            lfsr[ 1] <= lfsr[ 0]           ;
            lfsr[ 2] <= lfsr[ 1] ^ lfsr[10];
            lfsr[ 3] <= lfsr[ 2]           ;
            lfsr[ 4] <= lfsr[ 3] ^ lfsr[10];
            lfsr[ 5] <= lfsr[ 4] ^ lfsr[10];
            lfsr[ 6] <= lfsr[ 5] ^ lfsr[10];
            lfsr[ 7] <= lfsr[ 6]           ;
            lfsr[ 8] <= lfsr[ 7]           ;
            lfsr[ 9] <= lfsr[ 8]           ;
            lfsr[10] <= lfsr[ 9] ^ lfsr[10];
        end
    end

    always @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            data_buf <= 23'b0;
        end else if (start) begin
            data_buf <= data_in;
        end else if (busy) begin
            if (cnt >= 'd24 && cnt <= 'd46 && error_trapped_posedge) begin
                if (weight_of_s0 <= 'd3) begin
                    data_buf <= data_buf ^ {12'b0, s0};
                end else if (weight_of_s0_minus_s1 <= 'd2) begin
                    data_buf <= data_buf ^ {12'b0000_0010_0000, s0_minus_s1};
                end else if (weight_of_s0_minus_s2 <= 'd2) begin
                    data_buf <= data_buf ^ {12'b0000_0100_0000, s0_minus_s2};
                end
            end else if (cnt >= 'd1 && cnt <= 'd47) begin
                data_buf <= {data_buf[21:0], data_buf[22]};
            end 
        end


    end

    function [3:0] count_one;
        input  [10:0] data_in;
        count_one = data_in[0] + data_in[1] + data_in[2] + data_in[3] + data_in[4] +  data_in[5] + 
                    data_in[6] + data_in[7] + data_in[8] + data_in[9] + data_in[10];
    endfunction


endmodule